#include "stdafx.h"

#include "parser_state.h"
#include "lexer.h"
#include "parser_errors.h"

namespace funpl
{

namespace parser
{

// Gets the current line and character position.
void line_and_pos(size_t &line, size_t &pos, const parser_state &state)
{
	line = 0;
	pos = 0;

	// Count characters in current line & lines before current line
	for (const char *it = state.nextChar; it-- > state.firstChar; )
	{
		if (*it == '\n')
			++line;
		else if (line == 0)
			++pos;
	}

	// Revert current token
	pos -= lean::min(state.token.range.size(), pos);

	// 1-based
	++line;
	++pos;
}

// Gets the next token that has not yet been accepted.
parser_state& next_token(parser_state &state, bool implicitSemicolon)
{
	// Get next token that has not yet been accepted
	if (state.accepted)
	{
		state.firstNotAcceptedBegin = state.nextChar;

		state.token = parser::next_token(state.nextChar, true);
		state.accepted = false;

		state.firstNotAcceptedEnd = state.nextChar;
	}
	// Revert skipped implicit semicolons
	else if (implicitSemicolon && state.nextChar != state.firstNotAcceptedEnd)
	{
		state.nextChar = state.firstNotAcceptedBegin;
		state.token = parser::next_token(state.nextChar, true);
	}

	// Skip implicit semicolons, if requested
	if (!implicitSemicolon)
		while (state.token.type == token_type::tok_semicolon && state.token.range.empty())
			state.token = parser::next_token(state.nextChar, false);

	return state;
}

// Checks for the given token type, without accepting it.
bool check(parser_state &state, token_type::t type, bool implicitSemicolon)
{
	return (type == next_token(state, implicitSemicolon).token.type);
}

// Accepts the current token that MUST NOT have been accepted yet.
parser_state& accept(parser_state &state)
{
	LEAN_ASSERT_DEBUG(!state.accepted);

	state.accepted = true;

	// Acceptance breaks silence
	if (state.token.type != token_type::tok_semicolon)
		state.silent = false;

	return state;
}

// Accepts tokens of the given token type.
bool accept(parser_state &state, token_type::t type, bool implicitSemicolon)
{
	if (check(state, type, implicitSemicolon))
		accept(state);

	return state.accepted;
}

// Expects a token of the given token type.
parser_state& expect(parser_state &state, token_type::t type, bool implicitSemicolon)
{
	if (!accept(state, type, implicitSemicolon))
		FUNPL_PARSE_TOKEN_ERROR(
				state, debug::error_code::unexpected_token,
				"unexpeced token \"" << lean::from_range<std::string>(state.token.range)
					<< "\" (" << token_type_name(state.token.type)
					<< "), expected " << token_type_name(type)
			);

	return state;
}

// Skips all semicolons.
parser_state& skip_semicolons(parser_state &state)
{
	while (accept(state, token_type::tok_semicolon));
	return state;
}

} // namespace

} // namespace